package com.framework.websocket.redis;

import com.framework.websocket.core.GenericMessageHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Redis 事件管理器
 * 统一处理 Redis 键空间通知和消息分发
 */
@Slf4j
@Component
public class RedisEventManager implements MessageListener {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private RedisMessageListenerContainer listenerContainer;
    
    @Autowired
    private GenericMessageHandler messageHandler;
    
    /**
     * 监听的事件模式
     */
    private static final List<String> EVENT_PATTERNS = Arrays.asList(
        "__keyevent@*__:set",      // SET 命令
        "__keyevent@*__:expired",   // 键过期
        "__keyevent@*__:del",       // DEL 命令
        "__keyevent@*__:hset",      // HSET 命令
        "__keyevent@*__:hdel"       // HDEL 命令
    );
    
    @PostConstruct
    public void init() {
        // 确保 Redis 开启了键空间通知
        enableKeyspaceNotifications();
        
        // 注册监听器
        registerListeners();
        
        log.info("Redis 事件管理器初始化完成");
    }
    
    /**
     * 启用 Redis 键空间通知
     */
    private void enableKeyspaceNotifications() {
        try {
            redisTemplate.getConnectionFactory().getConnection()
                .setConfig("notify-keyspace-events", "KEA");
            log.info("已启用 Redis 键空间通知");
        } catch (Exception e) {
            log.warn("无法自动启用 Redis 键空间通知，请手动配置: notify-keyspace-events KEA", e);
        }
    }
    
    /**
     * 注册监听器
     */
    private void registerListeners() {
        for (String pattern : EVENT_PATTERNS) {
            PatternTopic topic = new PatternTopic(pattern);
            listenerContainer.addMessageListener(this, topic);
            log.info("注册 Redis 事件监听: {}", pattern);
        }
    }
    
    @Override
    public void onMessage(Message message, byte[] pattern) {
        try {
            String channel = new String(message.getChannel(), StandardCharsets.UTF_8);
            String key = new String(message.getBody(), StandardCharsets.UTF_8);
            
            log.debug("收到 Redis 事件 - Channel: {}, Key: {}", channel, key);
            
            // 处理键变化事件
            messageHandler.handleKeyChange(key);
        } catch (Exception e) {
            log.error("处理 Redis 事件失败", e);
        }
    }
    
    /**
     * 发布消息到 Redis
     */
    public void publish(String channel, Object message) {
        try {
            redisTemplate.convertAndSend(channel, message);
            log.debug("发布消息到 Redis - Channel: {}", channel);
        } catch (Exception e) {
            log.error("发布消息失败 - Channel: {}", channel, e);
        }
    }
    
    /**
     * 设置键值并设置过期时间
     */
    public void setWithExpire(String key, Object value, long seconds) {
        redisTemplate.opsForValue().set(key, value);
        if (seconds > 0) {
            redisTemplate.expire(key, seconds, java.util.concurrent.TimeUnit.SECONDS);
        }
    }
    
    /**
     * 批量设置键值
     */
    public void multiSet(Map<String, Object> map) {
        redisTemplate.opsForValue().multiSet(map);
    }
    
    /**
     * 删除键
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }
    
    /**
     * 批量删除键
     */
    public void deleteKeys(String pattern) {
        Set<String> keys = redisTemplate.keys(pattern);
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
    }
    
    @PreDestroy
    public void destroy() {
        log.info("Redis 事件管理器正在关闭");
    }
}